#pragma once 
#include <bits/stdc++.h>

/*****************************************************************************
 *! @brief 类型萃取提供了丰富的编译器计算、查询、判断、转换和选择的帮助类. 在很多场合会用到
 *!        这些特定. type_traits的类型选择功能，在一定程度上可以消除冗余的switch-case/
 *!        if-else语句，降低程序的圈复杂度.
 *
 * 
 * 
 * 
 *  
 ****************************************************************************/


// 1. C++11以前定义一个编译期常量
template<typename T>
struct GetLeftSize {
    static const int value = 1;      // 维护一个静态常量, 可以通过GetLeftSize::value 获取;
};

template<typename T>
struct GetRightSize {
    enum {
        RED   = 1,                  // 维护一个枚举常量, 可以通过GetLeftSize::value 获取;
        GREEN = 2
    };
};

// 2. 自定义一个编译器常量类
template<typename T, T v>
struct CompileConstant {
    static const T  value  = v;
    typedef      T  value_type;
    typedef CompileConstant<T, v> type;
 
    /**
     *! @brief 类型转换符， 即将该结构体对象转换为原始类型
     * 
     *      typedef CompileConstant<bool, true> bool_true;
     * 
     *      bool_true a;
     *      std::cout << bool(a) << std::endl;   // 1
     * @return value_type  返回值的原始类型
     */
    operator  value_type() const { return value; }
};


class A {};                           // 父类
class B : public A {};                // 子类
class C {};                           // 普通类

//! 创建一个对象时移除引用
template<typename T>
typename std::remove_reference<T>::type* create() {
    // typedef typename std::remove_reference<T>::type U;
    using U =  typename std::remove_reference<T>::type;
    return  new U();
}

//! 自带cv限定符的例子
// template<typename T>
// T* create_no_cv() {
//     return new T();
// }
// int* p = create_no_cv<const volatile int&>();    //! Error: 编译不通过，需要移除原始引用和cv限定符

// implement version 1
template<typename T>
typename std::remove_cv<typename std::remove_reference<T>::type>::type* create_no_cv_v1 () {
    using U = typename std::remove_cv<typename std::remove_reference<T>::type>::type;
    return new U();
}

// implement version 2
template<typename T>
typename std::decay<T>::type* create_no_cv_v2 () {
    /**
     *! @brief 对于普通类型来说，decay是移除引用+cv限定符，对于数组和函数，decay的规则是:
     * 
     *  1). 先移除T的引用，得到U -> remove_reference<T>::type;
     *  2). if is_array<U>::type == true, 修改type为: remove_extent<U>::type *;  将数组转为指针
     *  3). else if is_function<U>::type == true,  则将函数转为函数指针；
     *  4). else 修改类型为remove_cv<U>::type;
     */
    using U = typename std::decay<T>::type;
    return new U();
}


//! 返回类型后置
template<typename F, typename Args>
auto Func(F f, Args args) -> decltype(f(args)) {
    return f(args);
}


int fn(int a) {            
    return int(a); 
}

// 1. 限制函数的输出类型只能是数值型
template<class T>
typename std::enable_if<std::is_arithmetic<T>::value, T>::type foo(T t) {
    return t;
}

// 2. 限制函数的第二个参数只能是int
template<class T>
T foo2(T t, typename std::enable_if<std::is_integral<T>::value, int>::type = 0) {
    return t;
}

// 3. 限制模板参数类型
template<class T, class = typename std::enable_if<std::is_integral<T>::value, int>::type>
T foo3(T t) {
    return t;
}


// 4. enable_if默认第二个参数为void,如果函数没有返回值时可以省略
template<class T>
typename std::enable_if<std::is_arithmetic<T>::value>::type foo4(T t) {
    std::cout << typeid(T).name() << std::endl;
}

// 5. 使用enable_if降低代码的圈复杂度
template<typename T>
std::string tostring_v1(T val) {
    if (typeid(T) == typeid(int) || typeid(T) == typeid(float) || typeid(T) == typeid(double)) {
        std::stringstream ss;
        ss << val;
        return ss.str();
    } else {
        return val;
    }
}

template<typename T>
typename std::enable_if<std::is_arithmetic<T>::value, std::string> tostring_v2(T val) {
    return std::to_string(val);
}

template<typename T>
typename std::enable_if<std::is_same<T, std::string>::value, std::string> tostring_v2(T val) {
    return val;
}

int std_type_traits();